﻿#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "scene_logic.h"
#include "GameServer.h"
#include "ManagerHelper.h"
#include "../share/MessageDef.h"
#include "script_define.h"
#include "../share/GameHostManager.h"
#include "playerinfo_mgr.h"
#include "../share/proxyclient_service.h"
#include "sceneobj_manager.h"
#include "MapSlot.h"
#include "sceneobj_loop.h"
#include "../share/sql/mysql_connection.h"
#include "../share/sql/mysql_resultset.h"
#include "union.h"
#include "game_trigger.h"
#include "scene_event.h"

namespace pt = boost::property_tree;
void CSceneLogic::Initialize()
{
	save_timer_.TimerInit(1 * 1000);
	save_timer_.TimerStart();
	player_id_map_.clear();
	socket_id_map_.clear();
	m_pid2mapid.clear();
	player_destroy_.clear();

	if (!LoadGameData())
	{
		LOG_ERROR("default", "load game data err!");
		exit(-1);
	}
	CTriggerManager::GetInstance().InitTrigger();
}

Player* CSceneLogic::GetPlayerByPlayerID(int playerId)
{
	TObjIDMap::iterator iter = player_id_map_.find(playerId);
	if (iter == player_id_map_.end())
	{
		return NULL;
	}
	return GetPlayerByObjID(iter->second);
}

Player* CSceneLogic::GetPlayerByObjID(OBJ_ID_TYPE obj_id)
{
	return CGameObjManager::GetInstance().GetObjectByID< Player >(obj_id);
}

Player* CSceneLogic::GetPlayerBySocket(int socket)
{
	TObjIDMap::iterator it = socket_id_map_.find(socket);
	if (it == socket_id_map_.end())
	{
		return NULL;
	}

	return GetPlayerByObjID(it->second);
}

int CSceneLogic::PlayerEnterGame(Player& player)
{
	std::pair< TObjIDMap::iterator, bool > insert_pair = player_id_map_.insert(TObjIDMap::value_type(player.GetPlayerId(), player.get_id()));
	if (insert_pair.second == false)
	{
		LOG_ERROR("default","insert player %d %d %d into role_map faild", player.GetAccount(), player.GetPlayerId(), player.get_id());
		return ::common::ERR_UNKNOWN;
	}

	return ::common::SUCCEED;
}

int CSceneLogic::RegistePlayerSocket(int32_t fd_, int obj_id)
{
	std::pair< TObjIDMap::iterator, bool > insert_pair = socket_id_map_.insert(TObjIDMap::value_type(fd_, obj_id));
	if (insert_pair.second == false)
	{
		LOG_ERROR("default","insert  socket %d obj %d into map faild", fd_, obj_id);
	}

	return -1;
}

int CSceneLogic::UnRegistePlayerSocket(int32_t fd_)
{
	TObjIDMap::iterator iter = socket_id_map_.find(fd_);
	if (iter != socket_id_map_.end())
	{
		socket_id_map_.erase(iter);
	}
	return 0;
}

void CSceneLogic::SetPlayerInfoToCpp(int32_t playerId, std::string& playerinfo)
{	
	pt::ptree ptr3;
	std::istringstream is ( playerinfo );
	pt::read_json ( is, ptr3 );		
	bool ret = ptr3.empty();
	if ( ret )
	{
		LOG_ERROR("default","Set playerinfo to cpp err! read json err");
		return;
	}
	Player* player = CSceneLogic::GetInstance().GetPlayerByPlayerID(playerId);
	if ( player == NULL )
	{
		LOG_ERROR("default","Set player info to cpp err! player is null");
		return;
	}
	player->ReadDataFromPtree ( std::move ( ptr3 ) );
}

void CSceneLogic::SendMessageToOnlinePlayer(std::string * notify, uint32_t msgId)
{
	_safe_guard_begin;

	LOG_DEBUG("default","-- SendMessageToOnlinePlayer enter ------------------------");
	for (TObjIDMap::iterator it = player_id_map_.begin(); it != player_id_map_.end(); ++it)
	{
		Player * _tmp_p = GetPlayerByPlayerID(it->first);
		if (nullptr == _tmp_p)
		{
			continue;
		}

		if (_tmp_p->HasData())
		{
			_tmp_p->SendToClient(notify, msgId);
			LOG_DEBUG("default",">> Send %d to %d", msgId, _tmp_p->GetPlayerId());
		}
	}

	LOG_DEBUG("default","-- SendMessageToOnlinePlayer over ------------------------");

	_safe_guard_end;
}

void CSceneLogic::GetRecommendFriend(uint32_t playerid, const std::unordered_map<uint32_t, int32_t>& friend_map, std::set<uint32_t> recommend_list)
{
	TObjIDMap::iterator it = player_id_map_.begin();
	int32_t pos = RAND(player_id_map_.size() + 1);
	for (int32_t i = 0; i < pos && it != player_id_map_.end(); ++i) {
		++it;
	}
	for ( ; it != player_id_map_.end(); ++it )
	{
		if (it == player_id_map_.end()) {
			it = player_id_map_.begin();
		}
		Player* pplayer = GetPlayerByPlayerID(it->first);
		if ( pplayer == NULL )
		{
			continue;
		}
		Player& player = *pplayer;

		// 不在线的过滤
		if (player.GetSocketId() <= 0)
		{
			continue;
		}

		// 已经是好友的过滤
		auto iter = friend_map.find(player.GetPlayerId());
		if (iter != friend_map.end())
		{
			continue;
		}

		//过滤自己
		if (player.GetPlayerId() == playerid)
		{
			continue;
		}

		// 过滤掉非法的
		if (player.GetPlayerId() <= 0) {
			continue;
		}

		// 过滤掉不符合等级的
		if (player.GetLevel() <= 10)
		{
			continue;
		}

		recommend_list.insert(player.GetPlayerId());

		if (recommend_list.size() >= 12)
		{
			break;
		}
	}
}

void CSceneLogic::SetPlayerId2MidMap(uint32_t playerId, uint32_t mapid)
{
	m_pid2mapid[playerId] = mapid;
}

int32_t CSceneLogic::GetMapIdByPlayerId(int32_t playerId)
{
	TPlayerIDMapIDMap::iterator it = m_pid2mapid.find(playerId);
	if (it != m_pid2mapid.end())
	{
		return it->second;
	}
	return 0;
}

void CSceneLogic::SendGmMessageToOnlinePlayer(uint32_t tp, std::string msg, uint32_t ct)
{
	Json::Value m_jv;
	m_jv["msgid"] = common_server_chat_msg_notify;
	m_jv["tp"] = tp;
	m_jv["c"] = msg;
	m_jv["ct"] = ct;

	std::string str = JsonWriter(m_jv);

	SendMessageToOnlinePlayer(&str, common_server_chat_msg_notify);
}

void CSceneLogic::SendGmMarqueeToOnlinePlayer(uint32_t spn, uint32_t tp, std::string msg, uint32_t priority /*= 1*/)
{
	Json::Value m_jv;
	m_jv["msgid"] = common_notice_common_ret;
	m_jv["ret"] = Json::Value(0);
	m_jv["user1"] = Json::Value(msg.c_str());
	m_jv["user2"] = "";
	m_jv["user3"] = "";
	m_jv["opt1"] = Json::Value(spn);
	m_jv["opt2"] = Json::Value(tp);
	m_jv["opt3"] = Json::Value(priority); // 优先级 1～7 从高到低

	std::string str = JsonWriter(m_jv);

	SendMessageToOnlinePlayer(&str, common_notice_common_ret);
}

void CSceneLogic::update_time_to_online_player(unsigned int label_id)
{
	_safe_guard_begin;

	for (TObjIDMap::iterator it = player_id_map_.begin(); it != player_id_map_.end(); ++it)
	{
		Player * _tmp_p = GetPlayerByPlayerID(it->first);
		if (nullptr == _tmp_p)
		{
			continue;
		}
		_tmp_p->update_by_time(MapSlot::GetInstance().GetLuaManagerPtr(), label_id);
	}

	_safe_guard_end;
}

int CSceneLogic::PlayerLeaveGame(Player& player)
{
	LOG_INFO("default","Player %d %d %d Leave Scene, reason %d", player.GetAccount(), player.GetPlayerId(), player.get_id());
	
	int32_t _ret = MapSlot::GetInstance().GetLuaManagerPtr()->RunScriptFunction(LUA_PlayerCommonScript,
		LUA_PlayerLeaveGame, player.GetPlayerId());

	// 离开玩家所在场景
	if (player.player_state() == Player::STATE_GAME)
	{
		// 保存玩家数据
		player.PlayerSave();
	}
	player_leave_game_event(player);

	// 同步game在线玩家到game
	Json::Value jmsg;
	uint32_t account = player.GetAccount();
	jmsg["areaId"] = GameHostManager::GetInstance().GetServerAreaId();
	jmsg["account"] = account;
	jmsg["playerId"] = player.GetPlayerId();
	std::string send_str = JsonWriter(jmsg);
	send_2_db(game_2_db_login_out, send_str, account);

	if (player.GetSocketId() > 0)
	{
		Json::Value target;
		target["msgid"] = gate_close_client_fd_ret;
		target["_gate_fd"] = player.GetSocketId();
		player.SendToClient(target, gate_close_client_fd_ret);
		CloseTcpConnect(player.GetSocketId());
	}
	player.set_connected(false);
	player.SetSocketId(0);

	//从管理中移除这个实体;
	AddPlayerDestroy(player);

	return ::common::SUCCEED;
}

void CSceneLogic::CloseTcpConnect(int32_t fd)
{
	TObjIDMap::iterator it = socket_id_map_.find(fd);
	if (it != socket_id_map_.end())
	{
		socket_id_map_.erase(it);
	}

	// 关闭与客户端的连接
	//GameServer::GetInstance().client_close_socket(fd);

}

void CSceneLogic::OnPlayerDisconnect(int32_t fd)
{
	//玩家掉线，先不删掉玩家，只是将玩家的连接断开，过10分钟后才删除玩家实体
	Player* player = GetPlayerBySocket(fd);
	if ( player == NULL )
	{
		LOG_INFO("default","client diconnect cant find player fd[%d]", fd);
		return;
	}

	LOG_INFO("default","player[%d] is disconnect!", player->GetPlayerId());

	player_disconnect_event(*player);
	UnRegistePlayerSocket(fd);
	player->OnDisconnect();
	//PlayerLeaveGame(*player);

	return;
}

void CSceneLogic::AddPlayerDestroy(Player& player)
{
	TObjIDMap::iterator it = player_id_map_.find(player.GetPlayerId());
	if (it != player_id_map_.end())
	{
		player_id_map_.erase(it);
	}
	player.set_player_state(Player::STATE_WATT_DESTROY);
	player_destroy_.push_back(player.get_id());
}

void CSceneLogic::PlayerDestroyDoing()
{
	TPlayerDestroyVector::iterator it = player_destroy_.begin();
	for (; it != player_destroy_.end(); ++it)
	{
		OBJ_ID_TYPE obj_id = *it;
		Player* player = CGameObjManager::GetInstance().GetObjectByID< Player >(obj_id);
		if (player != NULL)
		{
			if (player->player_state() != Player::STATE_WATT_DESTROY) continue;
			CGameObjManager::GetInstance().DestoryObject(*it);
		}
	}

	player_destroy_.clear();
}

void CSceneLogic::KickPlayer(Player& player)
{
	Json::Value notify;
	notify[""];
}

void CSceneLogic::ServiceExitting()
{
	int save_count = 0;
	CGameObjManager::GetInstance().ObjManagerLoop(OBJTYPE_PLAYER, CSceneObjLoop::PlayerLoopLogout, &save_count);
	LOG_INFO("default","service exitting save %d", save_count);
	CSceneLogic:GetInstance ().SaveGameData ();
}

void CSceneLogic::SetPlayerSomeInfoToRedisDBByPlayerId(int32_t playerid, std::string field, std::string value, uint32_t bTable)
{
	_safe_guard_begin;
	if (MapSlot::GetInstance().GetLuaManagerPtr() != nullptr)
	{
		if (MapSlot::GetInstance().GetLuaManagerPtr()->TryLock())
		{
			MapSlot::GetInstance().GetLuaManagerPtr()->RunScriptFunction(LUA_PlayerManagerScript, LUA_SetPlayerSomeOneStringInfoToRedisDB
				, playerid, playerid, field.c_str(), value.c_str(), (int32_t)bTable);
		}
		else
		{
			LOG_ERROR("default", "TryLock FALSE! SetPlayerSomeInfoToRedisDBByPlayerId 1");
		}
	}
	_safe_guard_end;
}

void CSceneLogic::SetPlayerSomeInfoToRedisDBByPlayerId(int32_t playerid, std::string field, int32_t value)
{
	_safe_guard_begin;
	if (MapSlot::GetInstance().GetLuaManagerPtr() != nullptr)
	{
		if (MapSlot::GetInstance().GetLuaManagerPtr()->TryLock())
		{
			MapSlot::GetInstance().GetLuaManagerPtr()->RunScriptFunction(LUA_PlayerManagerScript, LUA_SetPlayerSomeOneNumberInfoToRedisDB
				, playerid, playerid, value, field.c_str());
		}
		else
		{
			LOG_ERROR("default", "TryLock FALSE! SetPlayerSomeInfoToRedisDB 2");
		}
	}
	_safe_guard_end;
}

bool CSceneLogic::SaveGameData ()
{
	//保存tb_base_game 信息
	SaveSurpriseRechargeData ();
	return true;
}

void CSceneLogic::SaveSurpriseRechargeData ()
{
	do 
	{
		auto luaPtr = MapSlot::GetInstance ().GetLuaManagerPtr ();
		if( !luaPtr )
		{
			LOG_ERROR ( "default", "Why no lua ptr?!" );
			break;
		}

		std::string retString = std::string ();
		if( luaPtr->TryLock () )
		{				
			retString = luaPtr->RunScriptFunctionStringRet ( LUA_GameServerScript, LUA_SaveSurpriseRechargeInfo2DB, "" );
			LOG_INFO ( "default", "Try lock: Run function to get surprise recharge info: %s", retString.c_str () );
		}
		else		
		{
			retString = luaPtr->RunScriptFunctionStringRet ( LUA_GameServerScript, LUA_SaveSurpriseRechargeInfo2DB, "" );
			LOG_INFO ( "default", "Run function to get surprise recharge info: %s", retString.c_str () );
		}

		Json::Value rechargeInfo;
		JsonReader ( retString, rechargeInfo );

		Json::Value request;
		uint32_t msgId = game_server_surprise_recharge_save_request;
		request[ "msgid" ] = msgId;
		request[ "surpriseRecharge" ] = rechargeInfo[ "surpriseRecharge" ];	
		auto sendStr = JsonWriter ( request );
		auto areaId = GameHostManager::GetInstance ().GetServerAreaId ();
		LOG_INFO ( "default", "Save surprise recharge info to mysql: %s", sendStr.c_str () );
		send_2_db ( msgId, sendStr, areaId );

	} while ( false );	
}

void CSceneLogic::CheckSendEmail4SurpriseRecharge ()
{
	do 
	{
		auto luaPtr = MapSlot::GetInstance ().GetLuaManagerPtr ();
		if( !luaPtr )
		{
			LOG_ERROR ( "default", "Why no lua ptr?!" );
			break;
		}

		if( luaPtr->TryLock () )
		{
			luaPtr->RunScriptFunction ( LUA_GameServerScript, LUA_SendEmail2AllRechargedPlayers );
			LOG_INFO ( "default", "TryLock: Run function to check send surprise recharge to players" );
		}
		else
		{
			luaPtr->RunScriptFunction ( LUA_GameServerScript, LUA_SendEmail2AllRechargedPlayers );
			LOG_INFO ( "default", "Run function to check send surprise recharge to players" );
		}

	} while ( false );
}

bool CSceneLogic::LoadGameData()
{
	std::string host = GameHostManager::GetInstance().GetGameDBIP();
	std::string prot = GameHostManager::GetInstance().GetGameDBProt();
	std::string user = GameHostManager::GetInstance().GetGameDBUser();
	std::string pwd = GameHostManager::GetInstance().GetGameDBPwd();
	std::string dbname = GameHostManager::GetInstance().GetGameDBName();
	int32_t areaId = GameHostManager::GetInstance().GetServerAreaId();
	sql::Connection conn;
	if (conn.Init(host, prot, user, pwd, dbname) == false)
	{
		LOG_WARN("default", "init db conn : %s:%s:%s:%s:%s failed", host.c_str(), prot.c_str(), user.c_str(), pwd.c_str(), dbname.c_str());
		return false;
	}
	char sql[512] = { 0 };
	snprintf(sql, sizeof(sql), "SELECT areaId, uniondata, surpriseRecharge FROM tb_base_game WHERE areaId = '%d';", areaId);
	sql::ResultSet  result_set;
	sql::ResultSet* res = conn.ExecuteQuery(sql, result_set);

	std::string strSurpriseRecharge = "";

	if (res == NULL)
	{
		LOG_WARN("default", "load game data (tb_base_game) failed!");
		return false;
	}
	if (result_set.next() == true)
	{
		int32_t areaId = res->getInt("areaId");
		std::string union_str = res->getStringStr("uniondata");
		
		if ( 0 != res->findColumn( "surpriseRecharge"  ) )
		{ 
			strSurpriseRecharge = res->getString ( "surpriseRecharge" );
		}
		
		Json::Value union_json;
		JsonReader(union_str, union_json);
		CGuildManager::GetInstance().LoadGuildData(union_json);
	}
	else
	{
		char insert_sql[512] = { 0 };
		snprintf ( insert_sql, sizeof ( insert_sql ), "insert into  tb_base_game(areaId, surpriseRecharge ) values(%d, %s);", areaId, strSurpriseRecharge.c_str() );
		conn.ExecuteInsert(insert_sql);
		CGuildManager::GetInstance().InitGuildData();
	}

	auto luaPtr = MapSlot::GetInstance ().GetLuaManagerPtr ();
	if( luaPtr )
	{
		if( luaPtr->TryLock () )
		{			
			luaPtr->RunScriptFunction ( LUA_GameServerScript, LUA_SetSurpriseRechargeInfo2Redis, strSurpriseRecharge.c_str() );
		}
		else
		{		
			luaPtr->RunScriptFunction ( LUA_GameServerScript, LUA_SetSurpriseRechargeInfo2Redis, strSurpriseRecharge.c_str () );
		}
	}

	result_set.close();
	conn.Close();

	return true;
}

void CSceneLogic::FillOnlinePlayer(int32_t activityid, int32_t max_times, int32_t count, int32_t minlv, int32_t maxlv, Json::Value& online_list, int32_t start)
{
	int32_t start_count = 0;
	int32_t add_count = 0;
	for (TObjIDMap::iterator it = player_id_map_.begin(); it != player_id_map_.end(); ++it)
	{
		Player* player = GetPlayerByPlayerID(it->first);
		if (NULL == player)
		{
			continue;
		}
		if (player->role_activity().GetJoinTimes(activityid) >= max_times )
		{
			continue;
		}
		if ((minlv != 0 && maxlv != 0 )&&
			(player->GetLevel() < minlv || player->GetLevel() > maxlv))
		{
			continue;
		}
		int32_t team_id = CTeamMgr::GetInstance().GetRoleTeamID(player->GetPlayerId());
		if ( team_id != 0 )
		{
			continue;
		}
		if (start_count < start )
		{
			start_count++;
			continue;
		}
		Json::Value playerbaseinfo;
		player->FillBaseInfoToJson(playerbaseinfo);
		online_list.append(playerbaseinfo);
		add_count++;
		if ( add_count >= count )
		{
			return;
		}
	}
}

void CSceneLogic::TickUpdate()
{
	int32_t cur_tick = CUR_FRAME;
	if ((last_player_tick_ + 20) >= cur_tick)
	{
		CGameObjManager::GetInstance().ObjManagerLoop(OBJTYPE_PLAYER, CSceneLogic::PlayerUpdate, NULL);
		last_player_tick_ = cur_tick;
		return;
	}
}

bool CSceneLogic::PlayerUpdate(::common::CObj& obj, void* param)
{
	CEntity& entity = (CEntity&)(obj);
	entity.TickUpdate();
	return true;
}

const TObjIDMap& CSceneLogic::GetOnlineSocket()
{
	return socket_id_map_;
}

void CPlayerLoopTimer::TimerActive()
{
	int num = 0;
	CGameObjManager::GetInstance().ObjManagerLoop(OBJTYPE_PLAYER, CSceneObjLoop::PlayerLoopTimeEvent, &num);
	//LOG_DEBUG("default","player loop save %d", num);

	TimerStart();
}
